local ST = LibStub("AceAddon-3.0"):GetAddon("SurgeTrack", true)
local STF = ST:NewModule("SurgeTrack_Frame")
local LBF = LibStub("LibButtonFacade", true)
local LSM = LibStub("LibSharedMedia-3.0", true)

function ST:LoadSTF()
 ST:EnableModule("SurgeTrack_Frame")
 ST:DisableModule("SurgeTrack_Frame")
end
function STF:OnEnable()
 STF:InitFrameData()
 STF:InitRoot()
 STF:InitFlash()
 STF:InitUnits()
 STF:InitGatherModeDialog()
 ST:FramePosition()
 ST:RootScale()
 ST:RootAlpha()
end
function STF:InitFrameData()
 ST.container = {}
 ST.position = {
  align = {
   ["RIGHT"] = "RIGHT",
   ["LEFT"] = "LEFT",
   ["TOP"] = "TOP",
   ["BOTTOM"] = "BOTTOM",
  },
  oppo = {
   ["RIGHT"] = "LEFT",
   ["LEFT"] = "RIGHT",
   ["TOP"] = "BOTTOM",
   ["BOTTOM"] = "TOP",
  },
  x = {
   ["RIGHT"] = -1,
   ["LEFT"] = 1,
   ["TOP"] = 0,
   ["BOTTOM"] = 0,
  },
  y = {
   ["RIGHT"] = 0,
   ["LEFT"] = 0,
   ["TOP"] = -1,
   ["BOTTOM"] = 1,
  },
 }
end
function STF:InitRoot()
 local root = CreateFrame("Frame" , "STRoot" , UIParent)
 root:SetBackdrop({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",})
 root:SetFrameStrata("BACKGROUND")
 root:SetPoint("CENTER", UIParent, "CENTER", 0, 100)
 root:SetBackdropColor(0,1,0,0)
 root:SetClampedToScreen(true)
 root:EnableMouse(false)
 root:RegisterForDrag("LeftButton")
 root:SetMovable(false)
 root:SetScript("OnDragStart", function() root:StartMoving() end)
 root:SetScript("OnDragStop", function() root:StopMovingOrSizing() ST:RootSavePosition() end)
 ST.root = root
end
function STF:InitFlash()
 local flash = CreateFrame("Frame", "STFlash" , UIParent)
 flash:SetFrameStrata("BACKGROUND")
 flash:SetAllPoints()
 flash:SetAlpha(0)
 ST.flash = flash

 local flashtex = flash:CreateTexture("STFlashtex" , "BACKGROUND")
 flashtex:SetTexture(LSM:Fetch("background", LSM:List("background")[ST.db.profile.flash.background]))
 flashtex:SetBlendMode("ADD")
 flashtex:SetAllPoints()
 flashtex:SetVertexColor(ST.db.profile.flash.color.r,ST.db.profile.flash.color.g,ST.db.profile.flash.color.b)
 ST.flashtex = flashtex
end
function STF:InitUnits()
 ST:CreateContainer(1,ST.db.profile.unit.amount)
end
function STF:InitGatherModeDialog()
 StaticPopupDialogs["GatherModeDialog"] = {
  text = "SurgeTrack is running in gather mode.\n\nTry to use an item, obtain/remove buffs/debuffs/enhancements, use an trinket or obtain effect from other players.\n\nWhen you finish this, press \"ESC\" key or press this button to quit gather mode.",
  button1 = "Disable Gather Mode",
  OnAccept = function()
   ST:GetModule("SurgeTrack_Aura", true):GatherMode(false)
  end,
  timeout = 0,
  whileDead = true,
  OnHide = function (self, data)
   if (ST.GATHER) then
    ST:GetModule("SurgeTrack_Aura", true):GatherMode(false)
   end
  end,
  hideOnEscape = true,
  timeout = 300,
 }
end
function ST:RootSavePosition()
 if not ST.root then return end
 local s = ST.root:GetEffectiveScale()
 ST.db.profile.root.x = select(1,ST.root:GetCenter()) * s
 ST.db.profile.root.y = select(2,ST.root:GetCenter()) * s
end

local function RootAnimation()

 if ST.ICONTEST then return end
 if not ST.db.profile.unit.centered_align then return end
 
 local maxN = math.min(ST.db.profile.unit.amount, getn(ST.active_item))
 local minN = 0
 
 local width = (ST.db.profile.unit.gap + 36) * ( ST.db.profile.unit.amount - 1 ) * abs(ST.position.x[ST.db.profile.unit.array]) + 36
 local height = (ST.db.profile.unit.gap + 36) * ( ST.db.profile.unit.amount - 1 ) * abs(ST.position.y[ST.db.profile.unit.array]) + 36
 local s = ST.root:GetEffectiveScale() or ST.db.profile.root.scale
 local x = ST.db.profile.root.x or select(1,self.frame.root:GetCenter()) * s
 local y = ST.db.profile.root.y or select(2,self.frame.root:GetCenter()) * s
 local offsetx = (width - ( ST.db.profile.unit.gap + 36) * (maxN + minN - 2 ) -36 ) * 0.5 * ST.position.x[ST.db.profile.unit.array]
 local offsety = (height - (ST.db.profile.unit.gap + 36) * (maxN + minN - 2 ) -36 ) * 0.5 * ST.position.y[ST.db.profile.unit.array]
 local destX = (x / s) + offsetx
 local destY = (y / s) + offsety
 ST.root:ClearAllPoints()
  
 if ST.db.profile.unit.animation.slide then
  local startX = select(1,ST.root:GetCenter()) or (x / s)
  local startY = select(2,ST.root:GetCenter()) or (y / s)
  local timeStamp = GetTime()
  local total = 1
  local timetotal = 0.2
  ST.root:SetScript("OnUpdate",
   function(_,elapsed)
    total = total + elapsed
    if total >= 0.01 then

     local timespend = GetTime() - timeStamp
     if timespend >= timetotal then
      ST.root:SetScript("OnUpdate",nil)
      ST.root:SetPoint("CENTER", UIParent, "BOTTOMLEFT", destX , destY)
      return
     else
      local realtimeX = startX + ( destX - startX ) * (timespend / timetotal) ^ 0.5
      local realtimeY = startY + ( destY - startY ) * (timespend / timetotal) ^ 0.5
      ST.root:SetPoint("CENTER", UIParent, "BOTTOMLEFT", realtimeX , realtimeY)
     end

     total = 0

    end
   end
  )
 else
  print(destX,destY)
  
  ST.root:SetPoint("CENTER", UIParent, "BOTTOMLEFT", destX , destY)
 end
end
local function showContainer(containerId)
 local i = containerId
 local item = ST.active_item[ST.container[i].spellId]

 if ST.container[i].timer then
 ST:CancelTimer(ST.container[i].timer.FadeOutFlash, true)
 ST:CancelTimer(ST.container[i].timer.CC, true)
 ST:CancelTimer(ST.container[i].timer.SpellUpdate, true)
 ST.container[i].timer = {}
 end
  if ST.db.profile.unit.animation.overlay_glow then
   ActionButton_ShowOverlayGlow(ST.container[i].bt)
  end
   
 ST.container[i].timer = {}
 if ST.db.profile.unit.animation.fadeinout then
  UIFrameFadeIn(ST.container[i].bt, 0.5, 0, 1)
 else
  ST.container[i].bt:Show()
 end

 if ST.db.profile.unit.animation.fade_on_expire and item.duration and item.duration > 0 then
  ST.container[i].timer.FadeOutFlash = ST:ScheduleTimer("FadeOutFlashFunc", item.duration-4, i)
 end

 if ST.db.profile.cc.enable and item.duration and item.duration > 0 then
  ST.container[i].timer.CCExpire = item.timestamp + item.duration
  ST.container[i].timer.CC = ST:ScheduleRepeatingTimer("CCFunc", 1, i)
 end

 if item.event == "COMBAT_TEXT_UPDATE" then
  ST.container[i].timer.SpellUpdate = ST:ScheduleRepeatingTimer("SpellUpdateFunc", 0.4, i)
 end
 --RootAnimation()
end
local function hideContainer(containerId)
 local i = containerId
 if ST.container[i].timer then
  ST:CancelTimer(ST.container[i].timer.FadeOutFlash, true)
  ST:CancelTimer(ST.container[i].timer.CC, true)
  ST:CancelTimer(ST.container[i].timer.SpellUpdate, true)
  ST.container[i].timer = nil
 end
 ST.container[i].cc:SetText(nil)
 ST.container[i].bt:SetAlpha(1)
  if ST.db.profile.unit.animation.overlay_glow then
   ActionButton_HideOverlayGlow(ST.container[i].bt)
  end
 ST.container[i].bt:Hide()
 --RootAnimation()
end
local function updateIcon(i,spellIcon)
 spellIcon = spellIcon or ""
 ST.container[i].icon:SetTexture(spellIcon)
end
local function updateName(i,spellName)
 if not ST.db.profile.name.enable then return end
 spellName = spellName or ""
 ST.container[i].name:SetText(spellName)
end
local function updateStack(i,amount)
 if not ST.db.profile.stack.enable then return end
 if amount == 0 or not ST.db.profile.stack.one and amount == 1 then amount = nil end
 ST.container[i].stack:SetText(amount)
end
local function updateCd(i,timestamp,duration)
 if ST.db.profile.cd.enable and timestamp and duration and (timestamp > 0) and (duration > 0) then
  ST.container[i].cd:SetCooldown(timestamp, duration)
 else
  ST.container[i].cd:Hide()
 end
end
local function IconTest(arg)
 ST.ICONTEST = arg
 if arg then
  ST:DeleteContainer(1,ST.db.profile.unit.amount)
  for i = 1 , ST.db.profile.unit.amount do
   if ST.container[i] then
   updateIcon(i, ST.IMG)
   ST.container[i].bt:Show()
   end
  end
  ST:RootScale()
 else
  ST:DeleteContainer( 1 , ST.db.profile.unit.amount )
 end
end

function ST:RootScale()
 if not ST.root then return end
 ST.root:SetScale(ST.db.profile.root.scale)
 local s = ST.root:GetEffectiveScale()
 ST.db.profile.root.x = ST.db.profile.root.x or select(1,ST.root:GetCenter()) * s
 ST.db.profile.root.y = ST.db.profile.root.y or select(2,ST.root:GetCenter()) * s
 ST.root:ClearAllPoints()
 ST.root:SetPoint("CENTER", UIParent, "BOTTOMLEFT", ST.db.profile.root.x / s, ST.db.profile.root.y / s)
end
function ST:RootAlpha()
 if not ST.root then return end
 ST.root:SetAlpha(ST.db.profile.root.alpha)
end
function ST:CreateContainer(m, n)
 if LBF then
  local group = LBF:Group(ST.NAME)
  group:Skin(ST.db.profile.style.skinid , ST.db.profile.style.gloss, ST.db.profile.style.backdrop, ST.db.profile.style.colors)
  LBF:RegisterSkinCallback(ST.NAME, ST.SkinCallback, ST)
 end

 for i= m , n do
  if ST.container[i] then
   ST.container[i].bt:Hide()
  else

   local bt = CreateFrame("Button", format("STButton%02d", i) , ST.root , "ActionButtonTemplate")
   bt:SetParent(ST.root)
   bt:SetClampedToScreen(true)
   bt:SetNormalTexture(nil)
   bt:EnableMouse(false)

   bt:Hide()

   local icon = _G[("%sIcon"):format(bt:GetName())]

   local cd = _G[("%sCooldown"):format(bt:GetName())]
   cd:SetReverse(true)
   cd:SetDrawEdge(true)
   
   cd.noCooldownCount = not ST.db.profile.cd.omnicc

   local stack = _G[("%sCount"):format(bt:GetName())]
   stack:SetPoint("BOTTOMRIGHT",bt,"BOTTOMRIGHT",0,0)

   local cc = _G[("%sHotKey"):format(bt:GetName())]
   cc:SetPoint("CENTER",bt,"CENTER",0,0)
   cc:SetTextColor(1,1,1)

   local name = bt:CreateFontString(format("STFontStringName%02d", i), "OVERLAY")
   name:SetTextColor(1,1,1)

   if LBF then LBF:Group(ST.NAME):AddButton(bt) end

   ST.container[i] = {}
   ST.container[i].bt = bt
   ST.container[i].icon = icon
   ST.container[i].cd = cd
   ST.container[i].stack = stack
   ST.container[i].cc = cc
   ST.container[i].name = name

   -- ST.container[i].trans = nil
   -- ST.container[i].scale = nil
   -- ST.container[i].alpha = nil

   -- ST.container[i].next = i-1
   -- ST.container[i].prep = i+1
   ST.container[i].spellid = nil

   ST:StringFont(i)
  end
 end
end
function ST:DeleteContainer(m, n)
 for i = m , n do
 if ST.container[i] then ST:releaseContainer(i,false) end
 end
end
function ST:StringFont(i)
 ST.container[i].name:SetFont(LSM:Fetch("font", LSM:List("font")[ST.db.profile.name.font]), ST.db.profile.name.size,"OUTLINE")
 ST.container[i].stack:SetFont(LSM:Fetch("font", LSM:List("font")[ST.db.profile.stack.font]), ST.db.profile.stack.size,"OUTLINE")
 ST.container[i].cc:SetFont(LSM:Fetch("font", LSM:List("font")[ST.db.profile.cc.font]), ST.db.profile.cc.size,"OUTLINE")
 ST.container[i].stack:SetTextColor(1,1,0.3)
 ST:NamePosition(i)
end
function ST:FramePosition()
 local width = (ST.db.profile.unit.gap + 36) * ( ST.db.profile.unit.amount - 1 ) * abs(ST.position.x[ST.db.profile.unit.array]) + 36
 local height = (ST.db.profile.unit.gap + 36) * ( ST.db.profile.unit.amount - 1 ) * abs(ST.position.y[ST.db.profile.unit.array]) + 36
 ST.root:SetWidth( width )
 ST.root:SetHeight( height )
 ST.queue[0] = {next = 1}
 for i= 1 , ST.db.profile.unit.amount do
  ST.queue[i] = {}
  ST.queue[i].prev = i-1
  ST.queue[i].next = i+1
  ST.container[i].bt:ClearAllPoints()
  if i == 1 then
   ST.container[i].bt:SetPoint( "CENTER" , ST.root , "CENTER" , ((ST.db.profile.unit.gap + 36) * ( i - 1 ) + 18 - 0.5 * width ) * ST.position.x[ST.db.profile.unit.array] , ((ST.db.profile.unit.gap + 36) * ( i - 1 ) + 18 - 0.5 * height ) * ST.position.y[ST.db.profile.unit.array])
  else
   ST.container[i].bt:SetPoint( "CENTER" , ST.container[i-1].bt , "CENTER" , (ST.db.profile.unit.gap + 36) * ST.position.x[ST.db.profile.unit.array] , (ST.db.profile.unit.gap + 36) * ST.position.y[ST.db.profile.unit.array] )
  end
 end
 ST.queue[ST.db.profile.unit.amount].next = nil
end
local function resetPosition(i)
 if not ST.queue[i].next then return end
 local temp = i

 if ST.queue[0].next == i then
  local width = (ST.db.profile.unit.gap + 36) * ( ST.db.profile.unit.amount - 1 ) * abs(ST.position.x[ST.db.profile.unit.array]) + 36
  local height = (ST.db.profile.unit.gap + 36) * ( ST.db.profile.unit.amount - 1 ) * abs(ST.position.y[ST.db.profile.unit.array]) + 36
  ST.container[ST.queue[i].next].bt:SetPoint( "CENTER" , ST.root , "CENTER" , ((ST.db.profile.unit.gap + 36) * ( 1 - 1 ) + 18 - 0.5 * width ) * ST.position.x[ST.db.profile.unit.array] , ((ST.db.profile.unit.gap + 36) * ( 1 - 1 ) + 18 - 0.5 * height ) * ST.position.y[ST.db.profile.unit.array])
 else
  ST.container[ST.queue[i].next].bt:SetPoint( "CENTER" , ST.container[ST.queue[i].prev].bt , "CENTER" , (ST.db.profile.unit.gap + 36) * ST.position.x[ST.db.profile.unit.array] , (ST.db.profile.unit.gap + 36) * ST.position.y[ST.db.profile.unit.array] )

 end
 
 ST.queue[ST.queue[i].prev].next = ST.queue[i].next
 ST.queue[ST.queue[i].next].prev = ST.queue[i].prev
 while ST.queue[i].next do
  i = ST.queue[i].next
 end
 
  ST.container[temp].bt:SetPoint( "CENTER" , ST.container[i].bt , "CENTER" , (ST.db.profile.unit.gap + 36) * ST.position.x[ST.db.profile.unit.array] , (ST.db.profile.unit.gap + 36) * ST.position.y[ST.db.profile.unit.array] )
 
 ST.queue[temp].next = nil
 ST.queue[temp].prev = i
 ST.queue[i].next = temp
end
function ST:releaseContainer(i,showNext)
 ST.container[i].spellId = nil
 updateIcon(i)
 updateName(i)
 updateCd(i)
 updateStack(i)
 hideContainer(i)
 resetPosition(i)
 ST:LDBUpdate(ST.IMG,ST.NAME)
 if showNext then
  if (table.maxn(ST.wait)) == 0  then return end
  for spellId,isStandby in pairs(ST.wait) do
   if not isStandby then return end
   ST.wait[spellId] = nil
   local commit_info = {}
   local mask = tonumber("111111",2)
   ST:updateItem(spellId,commit_info,mask)
   return
  end
 end
end
function ST:updateContainer(containerId,mask)
 local item = ST.active_item[ST.container[containerId].spellId]
 if not item then return end
 if floor(mask/tonumber("10",2))%2 == 1 then
  updateIcon(containerId,item.spellIcon)
  updateName(containerId,item.spellName)
 end
 if floor(mask/tonumber("100000",2))%2 == 1 then
  updateStack(containerId,item.amount)
 end
 if floor(mask/tonumber("10000",2))%2 == 1 then
  updateCd(containerId,item.timestamp,item.duration)
 end
 showContainer(containerId)
end
function ST:RootLock(v)
 ST.LOCK = v
 if v then
  ST.root:SetMovable(false)
  ST.root:EnableMouse(false)
  ST.root:SetBackdropColor(0,1,0,0)
  IconTest(false)
 else
  ST.root:SetMovable(true)
  ST.root:EnableMouse(true)
  ST.root:SetBackdropColor(0,1,0)
  IconTest(true)
 end
end
function ST:SkinCallback(skinid, gloss, backdrop, Group, Button, colors)
 ST.db.profile.style.skinid = skinid
 ST.db.profile.style.gloss = gloss
 ST.db.profile.style.backdrop = backdrop
 ST.db.profile.style.colors = colors
end
function ST:FadeOutFlashFunc(i)
 UIFrameFlash(ST.container[i].bt, 0.2, 0.4, 4, nil, 0, 0.2)
end
function ST:CCFunc(i)
 ST.container[i].cc:SetText(floor(ST.container[i].timer.CCExpire - GetTime()))
end
function ST:SpellUpdateFunc(i)
 local spellId = ST.container[i].spellid
 local spellName = ST.active_item[spellId].spellName
 if select(1,GetSpellCooldown(spellName)) ~= 0 then
  ST.container[i].bt:SetAlpha(0.2)
 else
  ST.container[i].bt:SetAlpha(1)
 end

 if not select(1, IsUsableSpell(spellName)) then
  ST.container[i].bt:SetAlpha(1)
  ST:releaseItem(spellId)
 end
end
function ST:NamePosition(i)

 local relativePoint = ST.db.profile.name.position
 local point = ST.position.oppo[relativePoint]
 local xOffset = (3 + 0.2 * ST.db.profile.name.size) * ST.position.x[point]
 local yOffset = (3 + 0.2 * ST.db.profile.name.size) * ST.position.y[point]
 ST.container[i].name:ClearAllPoints()
 ST.container[i].name:SetPoint(point,ST.container[i].bt,relativePoint,xOffset,yOffset)

end
function ST:doFullScrFlash()
 if not ST.db.profile.flash.enable then return end
 UIFrameFadeOut(ST.flash, ST.db.profile.flash.duration, ST.db.profile.flash.color.a, 0)
end

